﻿using CSScriptLib;
using Microsoft.CSharp;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using RAP.Framework.Libary.Utils;
using System.Collections.Concurrent;
using RAP.Framework.Libary.Lock;

namespace RAP.Framework.Libary.ExpressionCompiler
{
	/// <summary>
	/// CSharp表达式解析器
	/// </summary>
	public static class CSCompiler
	{
		private static long _asssemblyIdentity = 0;
		private static long AsssemblyIdentity
		{
			get
			{
				return Interlocked.Increment( ref _asssemblyIdentity );
			}
		}

		private static ConcurrentDictionary<string, object> _compileCaches = new ConcurrentDictionary<string, object>();

		public static string DefaultUsingNamespaces { get; } = @"
using System; 
using System.Collections;
using System.Collections.Generic;
using RAP.Framework.Libary.Utils;";

		/// <summary>
		/// 将表达式解析成TDelegate类型的Func或Action
		/// </summary>
		/// <typeparam name="TDelegate">委托类型</typeparam>
		/// <param name="code">表达式</param>
		/// <param name="parameterNames">参数名称</param>
		/// <returns></returns>
		public static TDelegate CompileDelegate<TDelegate>( string code, string paramNames, string usingNamespaces = null )
		{
			var typeDelegate = typeof( TDelegate );
			
			code = code.Trim();
			var cacheKey = $"Delegate,{typeDelegate.FullName},{code}";
			if ( !_compileCaches.TryGetValue( cacheKey, out dynamic compileResult ) )
			{
				GlobalLocks.Lock( cacheKey, ( ) =>
				{
					return _compileCaches.ContainsKey( cacheKey );
				}, ( ) =>
				{
					var delegateTypeName = typeDelegate.GenericTypeArguments.Join<Type>( ",", ( type, idx ) =>
					{
						return type.Name == "Object" ? "dynamic" : type.FullName;
					} );
					var isFunc = typeDelegate.Name.StartsWith( "Func`" );
					var resultType = typeDelegate.GenericTypeArguments.Last<Type>();
					var className = $"DynamicClass_{AsssemblyIdentity}";
					var endTag = code.EndsWith( ';' ) ? "" : ";";
					var methodType = isFunc ? "Func" : "Action";
					try
					{
						var compileCode = $@"
{DefaultUsingNamespaces}
{usingNamespaces}
public class {className}
{{
	public {methodType}<{delegateTypeName}> DynamicMethod = new {methodType}<{delegateTypeName}>( ( {paramNames} ) =>
	{{
		{resultType.FullName} value;
		{code}{endTag}
		{( isFunc ? "return value;" : "" )}
	}} );
}}";
						compileResult = CSScript.Evaluator.LoadCode( compileCode );
						_compileCaches.TryAdd( cacheKey, compileResult );
					}
					catch ( Exception ex )
					{
						Console.WriteLine( $"CSScript compile error:{ex.Message}" );
					}
				} );
			}
			return ( TDelegate )compileResult.DynamicMethod;
		}
	}
}
